# -*- coding: utf-8 -*-
from __future__ import annotations

import threading
import time
from typing import List, Dict

from core import threadLocal
from core.backtest import Backtest, BacktestMultiThread
from core.dataClasses import Event, TradeInfo, TacticResultData, SecurityHolder, SecurityGetter
from core.enums import Period, EventType, DateFormat, TradeType, ThreadName
from core.inventedtrader import InventedTrader, Trader
from core.securitySelection import SecuritySelectionMultiThread
from domain.transaction_data.repository import transaction_data_repository
from event.event_manager import EventManager
from infrastructure.util import dateutils
from tactic.ma import TradeBuyPointTactic, TradeSellPointTactic, MaNoticeTactic, MaUnnoticeTactic
from config.securities import *
from core import logger


class EventHandler:

    def __init__(self, lock: threading.Lock, shareDict: Dict[str, SecurityHolder], trader: Trader):
        self.lock: threading.Lock = lock
        self.shareDict: Dict[str, SecurityHolder] = shareDict
        self.trader: Trader = trader

    def __call__(self, e: Event):
        data: TacticResultData = e.data.data
        with self.lock:
            if e.type == EventType.TRADE_BUY and data.security not in self.shareDict[data.classify].ins:
                tradeInfo = TradeInfo().setLot(1).setTradeType(TradeType.BUY).setSecurity(data.security).setPrice(data.price).setTime(data.dt)
                if self.trader.testBuy(tradeInfo):
                    data.original = self.shareDict[data.sourceClassify].getInfo(data.security)
                    self.shareDict[data.sourceClassify] = self.shareDict[data.sourceClassify].delSecurity(data)
                    self.shareDict[data.classify] = self.shareDict[data.classify].addSecurity(data)
                    self.trader.trade(tradeInfo)
                else:
                    self.shareDict[data.sourceClassify] = self.shareDict[data.sourceClassify].setInfo(self.shareDict[data.sourceClassify].getInfo(data.security).setOpdt(data.dt))
            elif e.type == EventType.TRADE_SELL and data.security not in self.shareDict[data.classify].ins:
                tradeInfo = TradeInfo().setLot(1).setTradeType(TradeType.SELL).setSecurity(data.security).setPrice(data.price).setTime(data.dt)
                if self.trader.testSell(tradeInfo):
                    data.original = self.shareDict[data.sourceClassify].getInfo(data.security)
                    self.shareDict[data.sourceClassify] = self.shareDict[data.sourceClassify].delSecurity(data)
                    self.shareDict[data.classify] = self.shareDict[data.classify].addSecurity(data)
                    self.trader.trade(tradeInfo)
                else:
                    self.shareDict[data.sourceClassify] = self.shareDict[data.sourceClassify].setInfo(self.shareDict[data.sourceClassify].getInfo(data.security).setOpdt(dateutils.getNextPeriodDatetime(data.period, data.dt)))
            elif e.type in [EventType.TACTIC_MONITOR, EventType.TACTIC_UNMONITOR] and data.security not in self.shareDict[data.classify].ins:
                data.original = self.shareDict[data.sourceClassify].getInfo(data.security)
                self.shareDict[data.sourceClassify] = self.shareDict[data.sourceClassify].delSecurity(data)
                self.shareDict[data.classify] = self.shareDict[data.classify].addSecurity(data)


def primaryNoticeSelection(lock, shareDict: Dict[str, SecurityHolder], period: Period, getter: SecurityGetter, classify, sourceClassify, name: str):
    threadLocal.add(ThreadName.CLOCK.value, str(threading.current_thread().ident))
    threadLocal.add(ThreadName.CONTEXT.value, str(threading.current_thread().ident))
    event_manager: EventManager = EventManager.getInstance(name=name)
    eventHandler = EventHandler(lock, shareDict, InventedTrader.getInstance(name))
    event_manager.addEventListener(eventType=EventType.TACTIC_MONITOR, handler=eventHandler).addEventListener(eventType=EventType.TACTIC_UNMONITOR, handler=eventHandler)

    tactic = MaNoticeTactic(classify, sourceClassify).setPeriod(period=period)

    backtest = BacktestMultiThread().setGetter(getter).setTactic(tactic)

    backtest.start()


def primaryUnnoticeSelection(lock, shareDict: Dict[str, SecurityHolder], period: Period, getter: SecurityGetter, classify, sourceClassify, name: str):
    threadLocal.add(ThreadName.CLOCK.value, str(threading.current_thread().ident))
    threadLocal.add(ThreadName.CONTEXT.value, str(threading.current_thread().ident))
    event_manager: EventManager = EventManager.getInstance(name=name)
    eventHandler = EventHandler(lock, shareDict, InventedTrader.getInstance(name))
    event_manager.addEventListener(eventType=EventType.TACTIC_MONITOR, handler=eventHandler).addEventListener(eventType=EventType.TACTIC_UNMONITOR, handler=eventHandler)

    tactic = MaUnnoticeTactic(classify, sourceClassify).setPeriod(period=period)

    backtest = BacktestMultiThread().setGetter(getter).setTactic(tactic)

    backtest.start()


def pointOfBuyDetecting(lock, shareDict: Dict[str, SecurityHolder], period: Period, getter: SecurityGetter, classify, sourceClassify, unnoticeClassify, name: str):
    threadLocal.add(ThreadName.CLOCK.value, str(threading.current_thread().ident))
    threadLocal.add(ThreadName.CONTEXT.value, str(threading.current_thread().ident))
    event_manager: EventManager = EventManager.getInstance(name=name)
    eventHandler = EventHandler(lock, shareDict, InventedTrader.getInstance(name))
    event_manager.addEventListener(eventType=EventType.TRADE_BUY, handler=eventHandler)
    event_manager.addEventListener(eventType=EventType.TRADE_SELL, handler=eventHandler)

    tactic = TradeBuyPointTactic(classify, sourceClassify=sourceClassify, unnoticeClassify=unnoticeClassify).setPeriod(period=period)

    selection = SecuritySelectionMultiThread().setGetter(getter).setTactic(tactic)

    selection.start()


def pointOfSellDetecting(lock, shareDict: Dict[str, SecurityHolder], period: Period, getter: SecurityGetter, classify, sourceClassify, name: str):
    threadLocal.add(ThreadName.CLOCK.value, str(threading.current_thread().ident))
    threadLocal.add(ThreadName.CONTEXT.value, str(threading.current_thread().ident))
    event_manager: EventManager = EventManager.getInstance(name=name)
    eventHandler = EventHandler(lock, shareDict, InventedTrader.getInstance(name))
    event_manager.addEventListener(eventType=EventType.TRADE_BUY, handler=eventHandler)
    event_manager.addEventListener(eventType=EventType.TRADE_SELL, handler=eventHandler)

    tactic = TradeSellPointTactic(classify, sourceClassify=sourceClassify).setPeriod(period=period)

    selection = SecuritySelectionMultiThread().setGetter(getter).setTactic(tactic)

    selection.start()


def dataAcquire(securities: List[str], period: Period.day):
    for security in securities:
        transaction_data_repository.get_stock_transaction_data(security, period)



def seletion(sourceSecurities: List[str], holdSecurities: List[str]):
    name = "test"
    shareDict = dict()
    lock = threading.Lock()

    event_manager: EventManager = EventManager.getInstance(name=name)
    event_manager.start()

    threadLocal.add(ThreadName.CLOCK.value, str(threading.current_thread().ident))
    trader = InventedTrader.getInstance(name).setFund(15000)

    # storehouse
    st_init = "ST_INIT"
    st_notice = "ST_NOTICE"
    st_hold = "ST_HOLD"

    shareDict[st_init] = SecurityHolder().addSecurities(sourceSecurities)
    shareDict[st_notice] = SecurityHolder()
    shareDict[st_hold] = SecurityHolder().addSecurities(holdSecurities)

    # 获取数据
    # pDataAcquire = threading.Thread(target=dataAcquire, args=(securities, Period.minute30))
    # pDataAcquire.start()

    # pNoticePrimary = threading.Thread(target=primaryNoticeSelection, args=(lock, shareDict, Period.minute30, SecurityGetter(shareDict, st_init), st_notice, st_init, name))
    # pUnnoticePrimary = threading.Thread(target=primaryUnnoticeSelection, args=(lock, shareDict, Period.minute30, SecurityGetter(shareDict, st_notice), st_init, st_notice, name))
    pPointOfBuyDetecting = threading.Thread(target=pointOfBuyDetecting, args=(lock, shareDict, Period.minute30, SecurityGetter(shareDict, st_init), st_hold, st_init, st_init, name))
    pPointOfSellDetecting = threading.Thread(target=pointOfSellDetecting, args=(lock, shareDict, Period.minute30, SecurityGetter(shareDict, st_hold), st_init, st_hold, name))


    # pNoticePrimary.start()
    # pUnnoticePrimary.start()
    pPointOfBuyDetecting.start()
    pPointOfSellDetecting.start()

    while (True):
        logger.success(f"\n交易分析：\n{trader.evaluate()}\n买卖信息：\n{str(shareDict[st_hold])}")
        time.sleep(10)

def startSection():
    securities = []
    securities = securities + sh50
    securities = securities + sz50
    securities = securities + hs300

    holdSecurities = []

    seletion(securities, holdSecurities)


def addSecurities(securities):
    pass


if __name__ == "__main__":
    securities = []
    securities = securities + sh50
    securities = securities + sz50
    securities = securities + hs300

    holdSecurities = ['601038.SH', '600515.SH', '000776.SZ']

    seletion(securities, holdSecurities)
    # multiProc(["603501.SH"])